﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Diagnostics;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Threading;
using System.Globalization;
using System.Windows.Threading;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Microsoft.CSharp;

using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.PrettyPrinter;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver;

using SmallProgram.Controls;
using RaisingStudio.SmallProgram.Library;
using RaisingStudio.SmallBasicCompiler;
using SmallProgram.raisingstudio.smallprogram;


namespace SmallProgram
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            textEditor.ShowLineNumbers = true;

            Stream steam = typeof(MainWindow).Assembly.GetManifestResourceStream("SmallProgram.Template.cs");
            textEditor.Load(steam);
            textEditor.Focus();

            LoadReferences();
            LoadCompletionData();

            textEditor.TextArea.TextEntering += textEditor_TextArea_TextEntering;
            textEditor.TextArea.TextEntered += textEditor_TextArea_TextEntered;
            textEditor.TextInput += new TextCompositionEventHandler(textEditor_TextInput);
            textEditor.TextChanged += new EventHandler(textEditor_TextChanged);

            this.errorListControl.SetCaret = SetCaret;

            this.KeyDown += new KeyEventHandler(UserControlMain_KeyDown);

            textEditor.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.CSharp.CSharpIndentationStrategy(textEditor.Options);
            foldingStrategy = new BraceFoldingStrategy();
            if (foldingStrategy != null)
            {
                if (foldingManager == null)
                {
                    foldingManager = FoldingManager.Install(textEditor.TextArea);
                }
                foldingStrategy.UpdateFoldings(foldingManager, textEditor.Document);
            }


            DispatcherTimer foldingUpdateTimer = new DispatcherTimer();
            foldingUpdateTimer.Interval = TimeSpan.FromSeconds(2);
            foldingUpdateTimer.Tick += foldingUpdateTimer_Tick;
            foldingUpdateTimer.Start();
        }

        #region Folding
        FoldingManager foldingManager;
        AbstractFoldingStrategy foldingStrategy;

        void foldingUpdateTimer_Tick(object sender, EventArgs e)
        {
            if (foldingStrategy != null)
            {
                foldingStrategy.UpdateFoldings(foldingManager, textEditor.Document);
            }
        }
        #endregion

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
            {
                textEditor.FontSize += e.Delta;
            }
            base.OnMouseWheel(e);
        }


        void textEditor_TextChanged(object sender, EventArgs e)
        {
            this.documentModified = true;
            UpdateDocumentTitle();
        }

        void textEditor_TextInput(object sender, TextCompositionEventArgs e)
        {
        }

        void UserControlMain_KeyDown(object sender, KeyEventArgs e)
        {
            Console.WriteLine(e.Key);

            if (e.Key == Key.F5)
            {
                RunProgram();
            }
            else if (e.SystemKey == Key.Right && (Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
            {
                ShowComplectionWindow();
            }
        }

        #region Editor
        private bool documentModified = false;
        private List<ProgramCompletionData> completionData;
        private ImageSource typeImage;
        private ImageSource propertyImage;
        private ImageSource methodImage;
        private ImageSource eventImage;

        private void LoadCompletionData()
        {
            LoadCompletionImages();
            this.completionData = new List<ProgramCompletionData>();
            foreach (var assembly in this._referenceAssemblies)
            {
                foreach (var type in assembly.GetTypes())
                {
                    object[] smallProgramTypeAttributes = type.GetCustomAttributes(typeof(SmallProgramTypeAttribute), false);
                    if ((smallProgramTypeAttributes != null) && (smallProgramTypeAttributes.Length > 0))
                    {
                        if ((type.GetCustomAttributes(typeof(HideFromIntellisenseAttribute), false) == null) || (type.GetCustomAttributes(typeof(HideFromIntellisenseAttribute), false).Length == 0))
                        {
                            ProgramCompletionData typeCompletionData = new ProgramCompletionData { Text = type.Name };
                            typeCompletionData.Image = this.typeImage;
                            this.completionData.Add(typeCompletionData);
                            List<MethodInfo> specialMethods = new List<MethodInfo>();
                            foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.Static))
                            {
                                if ((propertyInfo.GetCustomAttributes(typeof(HideFromIntellisenseAttribute), false) == null) || (propertyInfo.GetCustomAttributes(typeof(HideFromIntellisenseAttribute), false).Length == 0))
                                {
                                    ProgramCompletionData memberCompletionData = new ProgramCompletionData();
                                    memberCompletionData.Text = propertyInfo.Name;
                                    memberCompletionData.Image = this.propertyImage;
                                    this.completionData.Add(memberCompletionData);
                                }

                                specialMethods.Add(propertyInfo.GetGetMethod());
                                specialMethods.Add(propertyInfo.GetSetMethod());
                            }
                            foreach (EventInfo eventInfo in type.GetEvents(BindingFlags.Public | BindingFlags.Static))
                            {
                                if ((eventInfo.GetCustomAttributes(typeof(HideFromIntellisenseAttribute), false) == null) || (eventInfo.GetCustomAttributes(typeof(HideFromIntellisenseAttribute), false).Length == 0))
                                {
                                    ProgramCompletionData memberCompletionData = new ProgramCompletionData();
                                    memberCompletionData.Text = eventInfo.Name;
                                    memberCompletionData.Image = this.eventImage;
                                    this.completionData.Add(memberCompletionData);
                                }

                                specialMethods.Add(eventInfo.GetAddMethod());
                                specialMethods.Add(eventInfo.GetRemoveMethod());
                            }
                            foreach (MethodInfo methodInfo in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
                            {
                                if ((methodInfo.GetCustomAttributes(typeof(HideFromIntellisenseAttribute), false) == null) || (methodInfo.GetCustomAttributes(typeof(HideFromIntellisenseAttribute), false).Length == 0))
                                {
                                    if (!specialMethods.Contains(methodInfo) && !methodInfo.Name.StartsWith("op_"))
                                    {
                                        ProgramCompletionData memberCompletionData = new ProgramCompletionData();
                                        memberCompletionData.Text = methodInfo.Name;
                                        memberCompletionData.Image = this.methodImage;
                                        this.completionData.Add(memberCompletionData);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        private void LoadCompletionImages()
        {
            this.typeImage = new BitmapImage(new Uri(@"/Resources/type.png", UriKind.RelativeOrAbsolute));
            this.propertyImage = new BitmapImage(new Uri(@"/Resources/property.png", UriKind.RelativeOrAbsolute));
            this.methodImage = new BitmapImage(new Uri(@"/Resources/method.png", UriKind.RelativeOrAbsolute));
            this.eventImage = new BitmapImage(new Uri(@"/Resources/event.png", UriKind.RelativeOrAbsolute));
        }

        string currentFileName;
        CompletionWindow completionWindow;
        ProgramInsightWindow insightWindow;

        void CloseExistingCompletionWindow()
        {
            if (completionWindow != null)
            {
                completionWindow.Close();
            }
        }

        void CloseExistingInsightWindow()
        {
            if (insightWindow != null)
            {
                insightWindow.Close();
            }
        }

        private void ShowInsightWindow()
        {
            CloseExistingInsightWindow();

            insightWindow = new ProgramInsightWindow(textEditor.TextArea);
            insightWindow.FontFamily = textEditor.FontFamily;
            insightWindow.FontStretch = textEditor.FontStretch;
            insightWindow.FontWeight = textEditor.FontWeight;
            insightWindow.FontStyle = textEditor.FontStyle;
            insightWindow.FontSize = textEditor.FontSize;
            // TODO: insightWindow.Items             

            insightWindow.Closed += delegate
            {
                insightWindow = null;
            };

            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
            {
                insightWindow.Show();
            }));
        }

        private void ShowComplectionWindow()
        {
            CloseExistingCompletionWindow();

            completionWindow = new CompletionWindow(textEditor.TextArea);
            completionWindow.FontFamily = textEditor.FontFamily;
            completionWindow.FontStretch = textEditor.FontStretch;
            completionWindow.FontWeight = textEditor.FontWeight;
            completionWindow.FontStyle = textEditor.FontStyle;
            completionWindow.FontSize = textEditor.FontSize;
            // provide AvalonEdit with the data:
            IList<ICompletionData> data = completionWindow.CompletionList.CompletionData;
            foreach (var completionData in this.completionData)
            {
                data.Add(completionData);
            }

            completionWindow.Closed += delegate
            {
                completionWindow = null;
            };

            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate
                {
                    completionWindow.Show();
                }));

            FilterByEditingWord(string.Empty);
        }
		

        void textEditor_TextArea_TextEntered(object sender, TextCompositionEventArgs e)
        {
            if (e.Text == ".")
            {
                // open code completion after the user has pressed dot:
                ShowComplectionWindow();
            }
        }

        void textEditor_TextArea_TextEntering(object sender, TextCompositionEventArgs e)
        {
            if (e.Text.Length > 0 && completionWindow != null)
            {
                if (!char.IsLetterOrDigit(e.Text[0]))
                {
                    // Whenever a non-letter is typed while the completion window is open,
                    // insert the currently selected element.
                    completionWindow.CompletionList.RequestInsertion(e);
                }
            }
            // do not set e.Handled=true - we still want to insert the character that was typed
        }

        private void FilterByEditingWord(string editingText)
        {
            if (completionWindow != null)
            {
                string editingWord = editingText;
                int startIndex = textEditor.SelectionStart;
                while ((--startIndex > 0) && (char.IsLetterOrDigit(textEditor.Document.Text[startIndex])))
                {
                    editingWord = textEditor.Document.Text[startIndex] + editingWord;
                }
                if (editingWord.Length > 0)
                {
                    Debug.WriteLine(editingWord);
                    completionWindow.CompletionList.SelectItem(editingWord);
                }
            }
        }

        private void SetCaret(int line, int column)
        {
            try
            {
                try
                {
                    textEditor.ScrollTo(line, column);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                }
                if (line < textEditor.LineCount)
                {
                    VisualLine visualLine = textEditor.TextArea.TextView.GetVisualLine(line);
                    if (visualLine != null)
                    {
                        int characterIndex = visualLine.StartOffset + column;
                        textEditor.Select(characterIndex, 0);
                    }
                }
                textEditor.Focus();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
        }

        private void UpdateDocumentTitle()
        {
            if (!string.IsNullOrEmpty(currentFileName))
            {
                this.Title = string.Format("Small Program - {0}{1}", currentFileName, documentModified ? "*" : "");
            }
            else
            {
                this.Title = "Small Program";
            }
        }
        #endregion

        #region File
        protected override void OnClosing(CancelEventArgs e)
        {
            e.Cancel = !AcceptDocumentChanges();
            base.OnClosing(e);
        }

        private bool AcceptDocumentChanges()
        {
            if (this.documentModified)
            {
                string message = "Would you like to save this document before closing?";
                string optionalContent = "\n\nThe program you are closing has been modified.\nChoose Yes if you would like to save it; No to discard the changes and Cancel to dismiss this dialog.";
                NotificationButtons notificationButtons = SmallProgram.Controls.MessageBox.Show(message, "Small Program", Title + optionalContent, NotificationButtons.Cancel | NotificationButtons.No | NotificationButtons.Yes, NotificationIcon.Information);
                if (notificationButtons == NotificationButtons.Yes)
                {
                    return this.SaveDocument();
                }
                return notificationButtons == NotificationButtons.No;
            }
            return true;
        }

        private bool SaveDocument()
        {
            try
            {
                if (string.IsNullOrEmpty(currentFileName))
                {
                    SaveFileDialog dlg = new SaveFileDialog();
                    dlg.Filter = "C# Files|*.cs";
                    dlg.DefaultExt = ".cs";
                    dlg.FileName = "SmallProgram.cs";
                    if (dlg.ShowDialog() ?? false)
                    {
                        currentFileName = dlg.FileName;
                    }
                    else
                    {
                        return false;
                    }
                }
                textEditor.Save(currentFileName);
                documentModified = false;
                UpdateDocumentTitle();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                return false;
            }
            return true;
        }

        private void FileNewClick(object sender, RoutedEventArgs e)
        {
            if (AcceptDocumentChanges())
            {
                currentFileName = null;
                Stream steam = typeof(MainWindow).Assembly.GetManifestResourceStream("SmallProgram.Template.cs");
                textEditor.Load(steam);
                documentModified = false;
                UpdateDocumentTitle();
            }
        }

        private void FileOpenClick(object sender, RoutedEventArgs e)
        {
            if (AcceptDocumentChanges())
            {
                OpenFileDialog dlg = new OpenFileDialog();
                dlg.CheckFileExists = true;
                if (dlg.ShowDialog() ?? false)
                {
                    currentFileName = dlg.FileName;
                    textEditor.Load(currentFileName);
                    documentModified = false;
                    UpdateDocumentTitle();
                }
            }
        }

        private void FileSaveClick(object sender, RoutedEventArgs e)
        {
            SaveDocument();
        }

        private void FileSaveasClick(object sender, RoutedEventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "C# Files|*.cs";
            dlg.DefaultExt = ".cs";
            dlg.FileName = "SmallProgram.cs";
            if (dlg.ShowDialog() ?? false)
            {
                currentFileName = dlg.FileName;
            }
            else
            {
                return;
            }
            textEditor.Save(currentFileName);
            documentModified = false;
            UpdateDocumentTitle();
        }
        #endregion

        #region Web
        static IProjectContent InternalGetRtDirPC(ProjectContentRegistry pcr, string name)
        {
            IProjectContent pc = pcr.GetProjectContentForReference(name,
                System.IO.Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), name + ".dll"));

            if (null == pc)
                throw new Exception("Assembly " + name + " was not found in Runtime directory.");

            return pc;
        }

        private void WebOpenClick(object sender, RoutedEventArgs e)
        {
            try
            {
                // check the status.
                if (AcceptDocumentChanges())
                {
                    currentFileName = null;

                    SmallProgram.Controls.MessageBox messageBox = new SmallProgram.Controls.MessageBox();
                    messageBox.Description = "Import a program from the web.";
                    messageBox.Title = "Small Program";
                    var textBox = new TextBox
                    {
                        Name = "textBox",
                        FontSize = 32.0,
                        FontWeight = FontWeights.Bold,
                        FontFamily = new FontFamily("Courier New"),
                        Foreground = Brushes.DimGray,
                        Margin = new Thickness(0.0, 4.0, 4.0, 4.0),
                        MinWidth = 300.0
                    };

                    StackPanel stackPanel = new StackPanel
                    {
                        Orientation = Orientation.Vertical,
                        Children =
                        {
                            new TextBlock
                            {
                                Text = "Please enter the program ID.",
                                Margin = new Thickness(0.0, 0.0, 4.0, 4.0)
                            },
                            textBox
                        }
                    };

                    messageBox.OptionalContent = stackPanel;
                    messageBox.NotificationButtons = (NotificationButtons.Cancel | NotificationButtons.OK);
                    messageBox.NotificationIcon = NotificationIcon.Information;
                    textBox.Focus();
                    if (messageBox.Display() == NotificationButtons.OK)
                    {
                        base.Cursor = Cursors.Wait;
                        string programID = textBox.Text.Trim();
                        if (!string.IsNullOrEmpty(programID))
                        {
                            int programId;
                            if (int.TryParse(programID, out programId))
                            {
                                LoadProgram(programID);
                            }
                            else
                            {
                                LoadSmallBasicProgram(programID);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                SmallProgram.Controls.MessageBox.Show("Failed to import program from the web.", "Small Program", string.Format(CultureInfo.CurrentUICulture, "We encountered the following error.\n{0}", new object[]
                {
                    ex.Message
                }), NotificationButtons.Close, NotificationIcon.Error);
            }
            finally
            {
                base.Cursor = Cursors.Arrow;
            }
        }

        private void LoadProgram(string programID)
        {
            Service service = new Service();
            string code = service.LoadProgram(programID);
            //Debug.WriteLine(code);

            if (code == "error")
            {
                SmallProgram.Controls.MessageBox.Show("Failed to import program from the web.", "Small Program", "The specified file was not found on the server.  Please check the ID and try again.", NotificationButtons.Close, NotificationIcon.Error);
            }
            else
            {
                textEditor.Text = code;
                documentModified = false;
                UpdateDocumentTitle();
            }
        }

        private void LoadSmallBasicProgram(string programID)
        {
            com.smallbasic.Service service = new com.smallbasic.Service();
            string code = service.LoadProgram(programID);
            //Debug.WriteLine(code);

            if (code == "error")
            {
                SmallProgram.Controls.MessageBox.Show("Failed to import program from the web.", "Small Program", "The specified file was not found on the server.  Please check the ID and try again.", NotificationButtons.Close, NotificationIcon.Error);
            }
            else
            {
                code = code.Replace("\n", "\r\n");
                //Debug.WriteLine(code);

                List<string> errors = new List<string>();
                Compiler compiler = new Compiler();
                compiler.Initialize();
                foreach (Error error in compiler.Compile(new StringReader(code)))
                {
                    errors.Add(string.Format("{0},{1}: {2}", error.Line + 1, error.Column + 1, error.Description));
                }
                if (errors.Count == 0)
                {
                    try
                    {
                        VisualBasicExporter exporter = new VisualBasicExporter(compiler);
                        string vbCode = exporter.GetProgramCode(compiler.Parser, "SmallProgram");
                        //Debug.WriteLine(vbCode);

                        // add vb imports.
                        vbCode = "Imports RaisingStudio.SmallProgram.Library\r\n" + vbCode;

                        ProjectContentRegistry pcr = new ProjectContentRegistry();
                        CodeSnippetConverter codeConverter = new CodeSnippetConverter()
                        {
                            ReferencedContents =
                                    {
                                        pcr.Mscorlib,
                                        InternalGetRtDirPC(pcr, "System"),
                                        InternalGetRtDirPC(pcr, "System.Core"),
                                        InternalGetRtDirPC(pcr, "Microsoft.VisualBasic"),
                                        pcr.GetProjectContentForReference(System.IO.Path.GetFileName(typeof(Primitive).Assembly.Location), typeof(Primitive).Assembly.Location)
                                    }
                        };
                        codeConverter.DefaultImportsToAdd = new List<string> { "RaisingStudio.SmallProgram.Library" };
                        string converterErrors;
                        string csharpCode = codeConverter.VBToCSharp(vbCode, out converterErrors);
                        //Debug.WriteLine(csharpCode);

                        string formattedCode = FormatCode(csharpCode);
                        //Debug.WriteLine(formattedCode);

                        textEditor.Text = formattedCode;
                        documentModified = false;
                        UpdateDocumentTitle();
                    }
                    catch (Exception ex)
                    {
                        System.Windows.MessageBox.Show(ex.Message);
                    }
                }
            }
        }

        private void WebSaveClick(object sender, RoutedEventArgs e)
        {
            try
            {
                base.Cursor = Cursors.Wait;
                Service service = new Service();
                string text = service.SaveProgram("", textEditor.Text, "");
                if (text == "error")
                {
                    SmallProgram.Controls.MessageBox.Show("Failed to publish program to the web.", "Small Program", "Your program was not published to the server.  It was either empty or too large.", NotificationButtons.Close, NotificationIcon.Error);
                }
                else
                {
                    new PublishProgramDialog(text, Properties.Settings.Default.LocationOfProgramHRef)
                    {
                        Owner = this
                    }.ShowDialog();
                }
            }
            catch (Exception ex)
            {
                SmallProgram.Controls.MessageBox.Show(ResourceHelper.GetString("FailedToPublishToWeb"), ResourceHelper.GetString("Title"), string.Format(CultureInfo.CurrentUICulture, "We encountered the following error.\n\n{0}", new object[]
		        {
			        ex.Message
		        }), NotificationButtons.Close, NotificationIcon.Error);
            }
            finally
            {
                base.Cursor = Cursors.Arrow;
            }
        }
        #endregion

        #region Run
        public static string GetAssemblyLocation(System.Reflection.Assembly[] assemblies, string fullName)
        {
            foreach (System.Reflection.Assembly assembly in assemblies)
            {
                if (assembly.FullName == fullName)
                {
                    return assembly.Location;
                }
            }
            return null;
        }

        private static void AddReferenceAssemblies(System.Type type, CompilerParameters compilerParameters)
        {
            System.Reflection.Assembly typeAssembly = type.Assembly;
            System.Reflection.AssemblyName[] assemblyNames = typeAssembly.GetReferencedAssemblies();
            System.Reflection.Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
            foreach (System.Reflection.AssemblyName assemblyName in assemblyNames)
            {
                string fullName = assemblyName.FullName;
                string assemblyLocation = GetAssemblyLocation(assemblies, fullName);
                if (assemblyLocation != null)
                {
                    compilerParameters.ReferencedAssemblies.Add(assemblyLocation);
                }
            }
            compilerParameters.ReferencedAssemblies.Add(typeAssembly.Location);
        }

        private void AddReferenceAssemblies(CompilerParameters compilerParameters)
        {
            foreach (var assembly in this._referenceAssemblies)
            {
                compilerParameters.ReferencedAssemblies.Add(assembly.Location);
            }
        }

        private void Run(object sender, RoutedEventArgs e)
        {
            RunProgram();
        }

        private void RunProgram()
        {
            string outputFileName;
            try
            {
                outputFileName = this.GetOutputFileName();
            }
            catch (Exception exception)
            {
                System.Windows.MessageBox.Show(exception.Message, "Small Program", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            this.errorListControl.Items.Clear();
            try
            {
                string code = textEditor.Document.Text;
                CodeDomProvider codeDomProvider = new CSharpCodeProvider();
                CompilerParameters compilerParameters = new CompilerParameters();
                //AddReferenceAssemblies(typeof(Program), compilerParameters);
                AddReferenceAssemblies(compilerParameters);

                //compilerParameters.GenerateInMemory = true;
                compilerParameters.OutputAssembly = outputFileName;
                compilerParameters.GenerateExecutable = true;
                compilerParameters.CompilerOptions = "/target:winexe";

                CompilerResults compilerResults = codeDomProvider.CompileAssemblyFromSource(compilerParameters, code);
                if (compilerResults.Errors.Count == 0)
                {
                    //System.Reflection.Assembly compiledAssembly = compilerResults.CompiledAssembly;
                    //RunAssembly(compiledAssembly);
                    RunProgram(outputFileName);
                }
                else
                {
                    foreach (CompilerError error in compilerResults.Errors)
                    {
                        this.errorListControl.Items.Add(string.Format("{0},{1}: {2}", error.Line, error.Column, error.ErrorText));
                    }
                }
            }
            catch (Exception ex)
            {
                this.errorListControl.Items.Add(ex.Message);
            }
        }

        private void OnProgramEnd(object sender, RoutedEventArgs e)
        {
            if ((this.currentProgramProcess != null) && !this.currentProgramProcess.HasExited)
            {
                this.currentProgramProcess.Kill();
                this.currentProgramProcess = null;
                this.programRunningOverlay.Visibility = Visibility.Hidden;
            }
        }

        public bool IsNewDocument
        {
            get
            {
                return (string.IsNullOrEmpty(currentFileName));
            }
        }

        public string DocumentTitle
        {
            get
            {
                return this.currentFileName;
            }
        }

        private string GetOutputFileName()
        {
            if (string.IsNullOrEmpty(currentFileName))
            {
                string tempFileName = System.IO.Path.GetTempFileName();
                System.IO.File.Move(tempFileName, tempFileName + ".exe");
                return (tempFileName + ".exe");
            }
            string fileNameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(currentFileName);
            return System.IO.Path.Combine(System.IO.Path.GetDirectoryName(currentFileName), fileNameWithoutExtension + ".exe");
        }

        private Process currentProgramProcess;
        private void RunProgram(string outputFileName)
        {
            string directoryName = System.IO.Path.GetDirectoryName(outputFileName);
            this.CopyLibraryAssemblies(directoryName);

            Thread.Sleep(500);
            this.currentProgramProcess = Process.Start(outputFileName);
            this.currentProgramProcess.EnableRaisingEvents = true;
            this.currentProgramProcess.Exited += delegate
            {
                this.currentProgramProcess = null;
                this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<object>((args) =>
                {
                    this.programRunningOverlay.Visibility = Visibility.Hidden;
                    if (IsNewDocument)
                    {
                        try
                        {
                            System.IO.File.Delete(outputFileName);
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex);
                        }
                    }
                    this.textEditor.Focus();
                }), null);
            };
            this.processRunningMessage.Text = string.Format("Your program ({0}) has started and is running.", DocumentTitle);
            this.programRunningOverlay.Visibility = Visibility.Visible;
            this.endProgramButton.Focus();
        }

        private void CopyLibraryAssemblies(string directory)
        {
            string location = typeof(Primitive).Assembly.Location;
            string fileName = System.IO.Path.GetFileName(location);
            try
            {
                System.IO.File.Copy(location, System.IO.Path.Combine(directory, fileName), true);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            foreach (string current in this._libraryFiles)
            {
                try
                {
                    fileName = System.IO.Path.GetFileName(current);
                    System.IO.File.Copy(current, System.IO.Path.Combine(directory, fileName), true);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                }
            }
        }

        public List<string> References
        {
            get
            {
                return this._libraryFiles;
            }
        }

        private List<Assembly> _referenceAssemblies;
        private List<string> _libraryFiles = new List<string>();

        private void LoadReferences()
        {
            PopulateClrSymbols();
            PopulateReferences();
        }

        private void PopulateReferences()
        {
            this._referenceAssemblies = new List<Assembly>();
            this._referenceAssemblies.Add(typeof(Primitive).Assembly);
            foreach (string current in this.References)
            {
                try
                {
                    Assembly item = Assembly.LoadFile(current);
                    if (item != null)
                    {
                        this._referenceAssemblies.Add(item);
                    }
                }
                catch
                {
                    throw new InvalidOperationException(string.Format("Unable to load reference {0}.", current));
                }
            }
        }

        private void PopulateClrSymbols()
        {
            string directoryName = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string path = System.IO.Path.Combine(directoryName, "lib");
            if (Directory.Exists(path))
            {
                string[] files = Directory.GetFiles(path, "*.dll");
                for (int i = 0; i < files.Length; i++)
                {
                    string text = files[i];
                    try
                    {
                        Assembly assembly = Assembly.LoadFile(text);
                        if (assembly != null)
                        {
                            this._libraryFiles.Add(text);
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex);
                    }
                }
            }
        }

        private void RunAssembly(Assembly compiledAssembly)
        {
            foreach (var type in compiledAssembly.GetTypes())
            {
                MethodInfo methodInfo = type.GetMethod("Main", BindingFlags.Public | BindingFlags.Static);
                if (methodInfo != null)
                {
                    methodInfo.Invoke(null, new object[] { });
                }
            }
        }
        #endregion

        private string previousLocation;
        private void Export(object sender, RoutedEventArgs e)
        {
            try
            {
                Stream stream = typeof(MainWindow).Assembly.GetManifestResourceStream("SmallProgram.Template.csproj");
                using (StreamReader reader = new StreamReader(stream))
                {
                    string projectTemplate = reader.ReadToEnd();
                    string projectFileContent = projectTemplate.Replace("$PROJECTGUID$", Guid.NewGuid().ToString());
                    System.Windows.Forms.FolderBrowserDialog folderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
                    if (previousLocation == null)
                    {
                        folderBrowserDialog.SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                    }
                    else
                    {
                        folderBrowserDialog.SelectedPath = previousLocation;
                    }
                    if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        previousLocation = folderBrowserDialog.SelectedPath;
                        string projectDirectory = previousLocation;
                        if (!System.IO.Directory.Exists(projectDirectory))
                        {
                            System.IO.Directory.CreateDirectory(projectDirectory);
                        }
                        string code = textEditor.Document.Text;
                        System.IO.File.WriteAllText(System.IO.Path.Combine(projectDirectory, "Application.cs"), code, System.Text.Encoding.UTF8);
                        string projectPath = System.IO.Path.Combine(projectDirectory, "SmallProgram.csproj");
                        string projectReferenceContent = GetProjectReferenceContent();
                        projectFileContent = projectFileContent.Replace("$PROJECTREFERENCE$", projectReferenceContent);
                        System.IO.File.WriteAllText(projectPath, projectFileContent, System.Text.Encoding.UTF8);
                        string libraryDirectory = System.IO.Path.Combine(projectDirectory, "lib");
                        if (!System.IO.Directory.Exists(libraryDirectory))
                        {
                            System.IO.Directory.CreateDirectory(libraryDirectory);
                        }
                        CopyLibraryAssemblies(libraryDirectory);
                        Thread.Sleep(500);

                        Process process = Process.Start(projectPath);
                        process.WaitForInputIdle();
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                System.Windows.MessageBox.Show(ex.Message, this.DocumentTitle, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private string GetProjectReferenceContent()
        {
            StringBuilder projectReferenceContent = new StringBuilder();
            string projectReferenceTemplate = "    <Reference Include=\"{0}\">\n      <HintPath>lib\\{0}</HintPath>\n    </Reference>";
            foreach (string current in this._libraryFiles)
            {
                try
                {
                    string fileName = System.IO.Path.GetFileName(current);
                    projectReferenceContent.AppendLine();
                    projectReferenceContent.AppendFormat(projectReferenceTemplate, fileName);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                }
            }
            return projectReferenceContent.ToString();
        }

        private void Find(object sender, ExecutedRoutedEventArgs e)
        {
        }

        private void FormatProgram()
        {
            string code = textEditor.Document.Text;
            code = FormatCode(code);
            if (code != textEditor.Document.Text)
            {
                textEditor.Document.Text = code;
                documentModified = true;
            }
        }

        private void HighlightProgram()
        {
            //TextDocument document = new TextDocument(code);
            //IHighlightingDefinition highlightDefinition = HighlightingManager.Instance.GetDefinition("C#");
            //IHighlighter highlighter = new DocumentHighlighter(document, highlightDefinition.MainRuleSet);
            //string html = HtmlClipboard.CreateHtmlFragment(document, highlighter, null, new HtmlOptions());

            //If you need more control over the output (e.g. if you want to add line numbers or alternating background color), you can also iterate through all lines and convert them individually:

            //for (int lineNumber = 1; lineNumber <= document.LineCount; lineNumber++) 
            //{
            //    HighlightedLine line = highlighter.HighlightLine(lineNumber);
            //    output.Append(line.ToHtml(new HtmlOptions()));
            //    output.AppendLine("<br>");
            //}
        }

        private static string FormatCode(string code)
        {
            IParser parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(code));
            parser.Parse();
            if (parser.Errors.Count == 0)
            {
                var compilationUnit = parser.CompilationUnit;
                CSharpOutputVisitor output = new CSharpOutputVisitor();
                output.Options.MethodBraceStyle = BraceStyle.NextLine;
                output.Options.NamespaceBraceStyle = BraceStyle.NextLine;
                output.Options.InterfaceBraceStyle = BraceStyle.NextLine;
                output.Options.StatementBraceStyle = BraceStyle.NextLine;
                output.Options.StructBraceStyle = BraceStyle.NextLine;
                output.Options.PropertySetBraceStyle = BraceStyle.NextLine;
                output.Options.PropertyGetBraceStyle = BraceStyle.NextLine;
                output.Options.PropertyBraceStyle = BraceStyle.NextLine;
                output.Options.EventRemoveBraceStyle = BraceStyle.NextLine;
                output.Options.EventBraceStyle = BraceStyle.NextLine;
                output.Options.EventAddBraceStyle = BraceStyle.NextLine;
                output.Options.DestructorBraceStyle = BraceStyle.NextLine;
                output.Options.ConstructorBraceStyle = BraceStyle.NextLine;
                output.Options.ClassBraceStyle = BraceStyle.NextLine;
                output.Options.AnonymousMethodBraceStyle = BraceStyle.NextLine;
                output.Options.EnumBraceStyle = BraceStyle.NextLine;

                compilationUnit.AcceptVisitor(output, null);
                return output.Text;
            }
            else
            {
                return code;
            }
        }
    }
}
